Skip to content

Fix logout race: clear partitioned cookies, recheck session on 401 - #4701

Merged
enzoames merged 5 commits into
mainfrom
ai_main_cf4fb118dee941018a53
Sep 11, 2026
Merged

enzoames merged 5 commits into
mainfrom
ai_main_cf4fb118dee941018a53

Conversation

@builder-io-integration

@builder-io-integration builder-io-integration Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the intermittent logout race where a user is briefly bounced to sign-in, then reappears signed in as the previous account with a "Something went wrong" error on the listing page.

Problem

After logout, the framework session cookie was deleted without matching the Partitioned/SameSite=None attributes it was originally set with under CHIPS. A partition-blind delete removes the wrong jar, so the browser keeps sending the old session cookie. If an instance's cached session state still resolves that stale token, the app can briefly re-authenticate as the previous account. Separately, when a request came back 401 (server no longer recognizes the browser), nothing told the client session gate, so the app shell stayed mounted on a stale "authenticated" answer and data queries surfaced a bare "Something went wrong" instead of redirecting to sign-in.

Solution

  • Make cookie deletion on logout mirror the exact attributes (including Partitioned) used when the cookie was set, so both the partitioned and unpartitioned jars are actually cleared.
  • Add a throttled session re-check that triggers whenever an authenticated request comes back 401, letting the client re-resolve the true session state and redirect to sign-in instead of showing a generic error.

Key Changes

  • deleteCookieFromEveryScope now applies crossSiteCookieAttrs(event) so cookie clears match the scope/partition they were set in; clearFrameworkSessionHintCookies updated accordingly.
  • Added recheckSessionAfterUnauthorized() in use-session.ts: throttled (5s min interval) re-fetch of session state, skipped once signingOut has begun to avoid resurrecting a session mid-logout.
  • use-action.ts calls recheckSessionAfterUnauthorized() on 401 responses (but not 403, which represents an authenticated caller being refused one specific action).
  • Added regression tests covering: partitioned vs. unpartitioned cookie clearing on logout (HTTPS and plain-HTTP), session re-check on 401 vs. no-op on 403, throttling of repeated re-checks, and re-check being a no-op once sign-out has started.
  • Added a changeset for @agent-native/core describing the cookie-partition fix and 401 re-resolution behavior.

Review follow-up (f6a26f72b)

Reviewer flagged that mirroring crossSiteCookieAttrs closed the partitioned jar but opened the mirror-image gap: a Partitioned-only delete cannot remove a cookie stored before CHIPS, or over plain HTTP on a host later served over HTTPS. Agreed and fixed — both variants are now emitted.

That needed a workaround. h3 dedupes set-cookie on name;domain;path and ignores Partitioned, but only sometimes: it computes the key from options when writing and re-parses the header when scanning, and the scan side never recovers domain. Probed directly:

case result
two deletes, same Domain, differing partition both survive
delete-then-set, same Domain delete survives alongside the set
delete-then-set, host-only deduped correctly

deleteCookieFromBothPartitions re-appends the unpartitioned delete only when h3 actually evicted it, so it is correct under either behaviour.

The middle row is pre-existing — 0dc7290a3 emits the same shape — and harmless, because a browser applies Set-Cookie in order and the set is last. Deliberately not fixed here; asserted instead, so no future change can leave a delete after the set. That assertion caught a duplicate domain-scoped delete in my first attempt at this follow-up.


Edit in Builder  Preview


To clone this PR locally use the Github CLI with command gh pr checkout 4701

You can tag me at @BuilderIO for anything you want me to fix or change

…cated

Logout deleted the framework session cookie without the Partitioned
attribute it was set with. Under CHIPS that empties the unpartitioned
jar, so the cookie survived logout and any instance whose session-email
cache still held the revoked token answered as the previous account.

Cookie clearing now mirrors crossSiteCookieAttrs, and the decision lives
in deleteCookieFromEveryScope so no caller can under-specify it. A 401
from an action also re-resolves the session, so a torn-down session
redirects to sign-in instead of painting a generic load error.
@builder-io-integration builder-io-integration Bot changed the title Fix logout leaving a live session cookie behind (CHIPS partition mismatch) Fix logout race: clear partitioned cookies, recheck session on 401 Sep 10, 2026
builder-io-integration[bot]

This comment was marked as outdated.

@steve8708

Copy link
Copy Markdown
Contributor

@builderio-bot look at the latest PR feedback and fix anything you agree with. Be skeptical. Reply on each comment thread whether you fixed it and why. Get CI green and keep the branch mergeable.

Mirroring crossSiteCookieAttrs closed the partitioned jar but opened the
other direction: a cookie stored before CHIPS, or over plain HTTP on a
host later served over HTTPS, survives a Partitioned-only delete.

h3 dedupes set-cookie on name/domain/path and ignores Partitioned, but
only evicts consistently for host-only cookies - with a Domain present
the scan side fails to match at all. Emit both variants and put the
unpartitioned one back only when h3 actually dropped it.
builder-io-integration[bot]

This comment was marked as outdated.

@builder-io-integration

Copy link
Copy Markdown
Contributor Author

There was a problem with your request, please try again later. Error id: a3f91c5e5ad649c2b8d160fa6da147ac

@github-actions

Copy link
Copy Markdown
Contributor

dispatch preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

design preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

analytics preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

assets preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

content preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

slides preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

forms preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

mail preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

calendar preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@github-actions

Copy link
Copy Markdown
Contributor

fw preview: Open preview

Built by GitHub Actions from f6a26f72be43257b06ed983f60416dd2c15e6bb4; Netlify is serving the uploaded artifact.

@builder-io-integration

Copy link
Copy Markdown
Contributor Author

CI status on f6a26f72b

69 pass / 3 fail / 13 skipped. All three failures are Netlify PR preview health checks — clips, plan, starter — and none are caused by this PR.

Each of the three reports the same shape:

OK   (/)                                    ← app boots and serves
FAIL (health): ready=false  /  HTTP 404     ← DB/health readiness on the preview site
OK   (jwks)                                 ← auth routes fine

Three reasons I'm confident this is infrastructure and not the change:

  1. The auth probe passes. The smoke test's --auth-routes check hits /_agent-native/auth/ba/jwks. That's the only auth surface it touches, it passes on all three, and this PR touches cookie deletion and a client-side session re-check — neither of which can move a health endpoint.
  2. It is not PR-specific. Run 34536074907 on an unrelated branch failed 12 of 26 previews on this same check, including clips, plan and starter plus design, mail, calendar and analytics. This PR failed 3 of 18.
  3. Other PRs look green because they deploy fewer previews. A template-scoped PR fans out to 1–2 preview deploys; this one touches packages/core, so all 18 run and the flake rate is visible. design — the template this bug was reported against — passed here, auth surface included.

Likely related to the PR-preview DB env wiring in fb312aa40 (#4724).

I tried to re-run the three failed jobs and got 403 Resource not accessible by integration, so that needs someone with workflow permissions — or they can just be left, since main's branch protection has no required status checks; the only thing gating merge is the 1 required approving review.

@enzoames

Copy link
Copy Markdown
Contributor

@builderio-bot Get CI green and keep the branch mergeable.

@builder-io-integration builder-io-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder reviewed your changes — looks good ✅

Review Details

Incremental Code Review Summary

The latest PR head contains no new code changes that introduce a confirmed defect. The partition cleanup workaround remains sound: it emits both partitioned and unpartitioned deletion variants, handles host-only and configured-domain scopes, and preserves Set-Cookie ordering so a fresh session token is not removed by a trailing delete. The client-side 401 revalidation remains throttled, avoids 403-induced sign-outs, and is suppressed after sign-out begins.

Two independent review agents found no new actionable issues. The previously reported unpartitioned-cookie issue was already fixed and resolved in the prior review. Focused regression tests were reported passing by the agents; the broader auth suite remains affected by an existing incomplete database mock/environment warnings.

Risk level remains high because this PR changes authentication and session invalidation behavior. Browser verification was attempted, but all executors lacked Chrome automation tools. The dev server/application endpoints were healthy, but no UI evidence could be captured.

🧪 Browser testing: Attempted after this review — dev server healthy, but Chrome automation tools were unavailable in the executor environment.

@enzoames
enzoames merged commit f17362f into main Sep 11, 2026
59 of 72 checks passed
@enzoames
enzoames deleted the ai_main_cf4fb118dee941018a53 branch September 11, 2026 21:20
@builder-io-integration

Copy link
Copy Markdown
Contributor Author

There was a problem with your request, please try again later. Error id: 9b2934010fd445faa895f76e1578a3da

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants